package net.sh.dpinfrastructure.mongo;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;

import com.mongodb.client.MongoCursor;


/**
 * 
 * mongo游标的包装类
 * 
 * @version 1.0
 * @since JDK1.7
 * @author shaohui
 * @company 上海朝阳永续信息技术有限公司
 * @copyright (c) 2016 SunTime Co'Ltd Inc. All rights reserved.
 * @date 2016年4月14日 上午10:57:37
 */
public class DPCursor {
    
    private MongoCursor<Document> cursor;
    
    /**
     * 构造函数
     * @param cursor 被包装的对象，不能为空
     */
    protected DPCursor(MongoCursor<Document> cursor) {
        if (cursor == null) {
            throw new NullPointerException();
        }
        this.cursor = cursor;
    }
    
    /**
     * 
     * 返回迭代器的下一个元素
     *
     * @return
     * 
     * @author shaohui
     * @date 2016年4月14日 上午11:08:44
     */
    public DPDocument next() {
        return new DPDocument(cursor.next());
    }
    
    /**
     * 
     * 检查是否还有下个元素
     *
     * @return
     * 
     * @author shaohui
     * @date 2016年4月14日 上午11:09:14
     */
    public boolean hasNext() {
        return cursor.hasNext();
    }
    
    /**
     * 关闭游标
     */
    public void close() {
        this.cursor.close();
    }
    
    /**
     * 
     * 获取被包装的游标类<br>
     * 获取原始mongo驱动结果集游标类，原则上尽量不要使用该类来进行相关mongo操作，因为会导致因为mongo升级带来的重构难度<br>
     * 当使用该包装类不易实现目标时可考虑该方法，该方法随时有可能被移除
     *
     * 
     * @author shaohui
     * @date 2016年4月14日 下午1:55:19
     */
    @Deprecated
    public MongoCursor<Document> toCursor() {
        return this.cursor;
    }
    
    /**
     * 
     * 将游标转换成需要的类型，返回后将关闭游标
     * 
     * @param mapper
     * @return
     *
     * @author shaohui
     * @date 2017年2月19日上午11:03:54
     * @since JDK 1.8
     */
    public <T> List<T> toList(DPResultMapper<T> mapper) {
        try {
            List<T> resultList = new ArrayList<>();
            while(this.hasNext()) {
                DPDocument document = this.next();
                resultList.add(mapper.mapper(document));
            }
            return resultList;
        } finally {
            this.close();
        }
    }
    
    /**
     * 
     * 将游标转换成需要的类型，返回后将关闭该游标
     * 
     * @param clazz
     * @return
     *
     * @author shaohui
     * @throws IllegalAccessException 
     * @throws InstantiationException 
     * @date 2017年2月19日下午12:15:56
     * @since JDK 1.8
     */
    public <T> List<T> toList(Class<T> clazz) throws InstantiationException, IllegalAccessException {
        try {
            Field[] fields = clazz.getDeclaredFields();
            
            // 类的字段和列名的映射
            Map<Field, String> fieldColumnNameMap = new HashMap<>();
            for (Field field : fields) {
                Column column = field.getAnnotation(Column.class);
                String columnName = column == null ? null : column.name();
                if (StringUtils.isBlank(columnName)) {
                    columnName = field.getName();
                }
                fieldColumnNameMap.put(field, columnName);
            }
            List<T> resultList = new ArrayList<>();
            while(this.hasNext()) {
                DPDocument document = this.next();
                T object = clazz.newInstance();
                for (Field field : fields) {
                    String columnName = fieldColumnNameMap.get(field);
                    Object value = document.get(columnName);
                    if (value == null) {
                        continue;
                    }
                    field.set(object, value);
                }
                resultList.add(object);
            }
            return resultList;
        } finally {
            this.close();
        }
    }
    
    /**
     * 
     * 将游标转换成map，返回后将关闭游标
     * 
     * @return
     *
     * @author shaohui
     * @date 2017年2月19日上午11:05:50
     * @since JDK 1.8
     */
    public List<Map<String, Object>> toList() {
        try {
            List<Map<String, Object>> resultList = new ArrayList<>();
            while(this.hasNext()) {
                DPDocument document = this.next();
                resultList.add(document);
            }
            return resultList;
        } finally {
            this.close();
        }
    }
    
    /**
     * 
     * 将游标转换成map，并将原始字段名映射成别名，返回后关闭游标
     * 
     * @param fieldsMapping
     * @return
     *
     * @author shaohui
     * @date 2017年2月19日上午11:11:43
     * @since JDK 1.8
     */
    public List<Map<String, Object>> toList(Map<String, String> fieldsMapping) {
        if (MapUtils.isEmpty(fieldsMapping)) {
            throw new IllegalArgumentException("empty field mapping");
        }
        List<Map<String, Object>> resultList = new ArrayList<>();
        try {
            while(this.hasNext()) {
                Map<String, Object> resultMap = new HashMap<>();
                DPDocument document = this.next();
                for (Map.Entry<String, Object> entry : document.entrySet()) {
                    String originField = entry.getKey();
                    String alias = fieldsMapping.get(originField);
                    if (StringUtils.isEmpty(alias)) { // 没指定别名则使用原字段名
                        alias = originField;
                    }
                    resultMap.put(alias, entry.getValue());
                }
                resultList.add(resultMap);
            }
            return resultList;
        } finally {
            this.close();
        }
    }
}
